#include "ui_graph.h"
#include "ui_manager.h"

//graph specific includes

#include "math/interpolation.h"


//end of graph specific includes


#include "audio/audio.h"
#include "graphics/graphics_util.h"

#include "util/timer.h"		    // Needs to come in early
#include "util/mmgr.h"		    // Needs to come in early
#include "util/common_macros.h"		// macros
#include "util/log.h"		    // logging

#include "script/scripthelper.h"
#include "script/write_lua.h"

#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#endif // _ENABLE_LUABIND

using namespace ui;
using namespace script;
using namespace LuaPlus;


//UIGraph: Constructor and destructor
//======================================================================================================================

UIGraph::UIGraph(UIWidget* pParent,const std::string& Name):
	UIWidget(pParent, Name),
	m_HeightSpeed(1.0f),
	m_HeightAxel(1.0f),
	m_CurHeightSpeed(m_HeightSpeed),
	m_RangeAuto(true),
	m_Range(0.0f, 100.0f),
	m_TLOffset(0.0f, 0.0f),
	m_BROffset(0.0f, 0.0f),
	m_Nodes(2, 0.0f),
	m_ZeroHeight(0.0f),
	m_GraphPointSize(4.0f/(float)GraphicsUtil::WIDTH,4.0f/(float)GraphicsUtil::HEIGHT),
	m_GraphColumnWidth(1.0f/(float)GraphicsUtil::WIDTH),
	m_GraphColumnColor(1.0f,1.0f,1.0f,1.0f),
	m_GraphColumnEvery(1000),
	m_GraphLevelHeight(1.0f/(float)GraphicsUtil::HEIGHT),
	m_GraphLevelYShift(0.0f),
	m_GraphMarkSize(8.0f/(float)GraphicsUtil::WIDTH,8.0f/(float)GraphicsUtil::HEIGHT), 
	m_SpriteGraphFill(),
	m_SpriteGraphPoint(),
	m_SpriteGraphColumn(),
	m_SpriteGraphLevel(),
	m_SpriteGraphMark(),
	m_GraphLevels(0),
	m_GraphMarks(0),
	m_GraphTL(0.0f, 0.0f),
	m_GraphSize(0.0f, 0.0f),
	m_GraphRectValid(false),
	m_CalculatedNodes(2, 0.0f),
	m_CurAnimFactor(0.0f),
	m_CurGraphState(GS_DROPPED)
{

	m_Type = WT_GRAPH;

}//UIGraph::UIGraph(UIWidget* pParent,const std::string& Name = "")




UIGraph::~UIGraph()
{

	//params uninit

}//UIGraph::~UIGraph()

//UIGraph: Common routines
//======================================================================================================================



bool UIGraph::Create(const std::string& Name, const math::Vec2& Pos, const math::Vec2& Size)
{

	UIWidget::Create(Name, Pos, Size);
	// create progress stuff

	return true;


}//bool UIGraph::Create(const std::string& Name, const math::Vec2& Pos, const math::Vec2& Size)




bool UIGraph::Load(LuaPlus::LuaObject& _LObj)
{

	UIWidget::Load(_LObj);	// get all the common stuff loaded
	
	//graph-specific stuff



	//heigh raising/dropping speed and axel
	m_HeightSpeed = ScriptHelper::GetTableFloat(_LObj, "HeightSpeed", 1.0f);
	if (m_HeightSpeed <= 0.0f)
		_LOG(MSG_WARNING, "UIGraph: height speed is <= 0! Graph will not rise!");

	m_HeightAxel = ScriptHelper::GetTableFloat(_LObj, "HeightAxeleration", 1.0f);
	if (m_HeightAxel < 0.0f)
		_LOG(MSG_WARNING, "UIGraph: height axel is < 0! Graph may not rise!");

	m_CurHeightSpeed = m_HeightSpeed;


	//Offsets
	m_TLOffset = ScriptHelper::GetTableVec2(_LObj, "TLOffset", math::Vec2(0.0f, 0.0f));
	m_BROffset = ScriptHelper::GetTableVec2(_LObj, "BROffset", math::Vec2(0.0f, 0.0f));

	//range
	m_RangeAuto = ScriptHelper::GetTableBool(_LObj, "RangeAuto", true);
	m_Range = ScriptHelper::GetTableVec2(_LObj, "Range", math::Vec2(0.0f, 100.0f)); 


	//nodes
	LuaObject NodesTable = ScriptHelper::GetObj(_LObj, "Nodes");

	int i;
	int NodesCount = NodesTable.GetCount();

	if (NodesCount > 1)
	{
		m_Nodes.resize(NodesCount);

		for (i = 0; i < NodesTable.GetCount(); i++)
		{
			LuaPlus::LuaObject TempObj = NodesTable[i+1];
			m_Nodes.at(i) = ScriptHelper::GetFloat(TempObj);
		}
	}



	//zero height
	m_ZeroHeight = ScriptHelper::GetTableFloat(_LObj, "ZeroHeight");



	//graph fill sprite settings
	//graph image
	std::string GraphFillImage = ScriptHelper::GetTableString(_LObj, "GraphFillImage");

	if (GraphFillImage != "")
		m_SpriteGraphFill.SetImage(GraphFillImage);

	//graph color
	math::Vec4 GraphFillColor  = ScriptHelper::GetTableVec4(_LObj, "GraphFillColor", math::Vec4(1, 1, 1, 1));

	m_SpriteGraphFill.SetColor(ColorfRGBA(GraphFillColor.x, GraphFillColor.y, GraphFillColor.z, GraphFillColor.w)); 

	//additive state
	m_SpriteGraphFill.SetAdditive(ScriptHelper::GetTableBool(_LObj, "GraphFillAdditive", false)); 



	//graph point sprite settings
	//graph image
	std::string GraphPointImage = ScriptHelper::GetTableString(_LObj, "GraphPointImage");

	if (GraphPointImage != "")
		m_SpriteGraphPoint.SetImage(GraphPointImage);

	//graph color
	math::Vec4 GraphPointColor  = ScriptHelper::GetTableVec4(_LObj, "GraphPointColor", math::Vec4(1, 1, 1, 1));

	m_SpriteGraphPoint.SetColor(ColorfRGBA(GraphPointColor.x, GraphPointColor.y, GraphPointColor.z, GraphPointColor.w)); 

	//additive state
	m_SpriteGraphPoint.SetAdditive(ScriptHelper::GetTableBool(_LObj, "GraphPointAdditive", false)); 

	//size
	m_GraphPointSize = ScriptHelper::GetTableVec2(_LObj, "GraphPointSize", math::Vec2(4.0f,4.0f)); 



	//graph column sprite settings
	//image
	std::string GraphColumnImage = ScriptHelper::GetTableString(_LObj, "GraphColumnImage");

	if (GraphColumnImage != "")
		m_SpriteGraphColumn.SetImage(GraphColumnImage);

	//color
	m_GraphColumnColor = ScriptHelper::GetTableVec4(_LObj, "GraphColumnColor", math::Vec4(1.0f,1.0f,1.0f,1.0f));

	//width
	m_GraphColumnWidth = ScriptHelper::GetTableFloat(_LObj, "GraphColumnWidth", 1.0f/(float)GraphicsUtil::WIDTH);

	//column every Nth node
	m_GraphColumnEvery = ScriptHelper::GetTableInt(_LObj, "GraphColumnEvery", 1000);

	//additive
	m_SpriteGraphColumn.SetAdditive(ScriptHelper::GetTableBool(_LObj, "GraphColumnAdditive", false)); 




	//graph level sprite settings
	//image
	std::string GraphLevelImage = ScriptHelper::GetTableString(_LObj, "GraphLevelImage");

	if (GraphLevelImage != "")
		m_SpriteGraphLevel.SetImage(GraphLevelImage);

	//height
	m_GraphLevelHeight = ScriptHelper::GetTableFloat(_LObj, "GraphLevelHeight", 1.0f/(float)GraphicsUtil::HEIGHT);

	//y shift
	m_GraphLevelYShift = ScriptHelper::GetTableFloat(_LObj, "GraphLevelYShift", 0.0f); 



	//graph mark sprite settings
	//image
	std::string GraphMarkImage = ScriptHelper::GetTableString(_LObj, "GraphMarkImage");

	if (GraphMarkImage != "")
		m_SpriteGraphMark.SetImage(GraphMarkImage);

	//size
	m_GraphMarkSize = ScriptHelper::GetTableVec2(_LObj, "GraphMarkSize", math::Vec2(8.0f/(float)GraphicsUtil::WIDTH,8.0f/(float)GraphicsUtil::HEIGHT)); 

	//FINALLY: making initial calculations
	RecalcGraphRect();
	RecalcNodes(); 

	return true;
}//bool UIGraph::Load(LuaPlus::LuaObject& _LObj)








//UIGraph: Tick and Render
//======================================================================================================================


bool UIGraph::Tick(float dt)
{
	UIWidget::Tick(dt);	// tick children, etc

	if (m_CurGraphState == GS_RISING)
	{

		if (m_CurAnimFactor < 1.0f)
		{
			m_CurHeightSpeed += m_HeightAxel * dt;

			m_CurAnimFactor += m_CurHeightSpeed * dt;

			 

		}

		if (m_CurAnimFactor >= 1.0f)
		{
			m_CurAnimFactor = 1.0f;
			m_CurGraphState = GS_RISEN;
			TriggerEvent(WE_GRAPH_RISEN);
		}
		RecalcNodes();
	}

	if (m_CurGraphState == GS_DROPPING)
	{
		if (m_CurAnimFactor > 0.0f)
		{
			m_CurHeightSpeed += m_HeightAxel * dt;

			m_CurAnimFactor -= m_CurHeightSpeed * dt;

			

		}

		if (m_CurAnimFactor <= 0.0f)
		{
			m_CurAnimFactor = 0.0f;
			m_CurGraphState = GS_DROPPED;
			TriggerEvent(WE_GRAPH_DROPPED);
		}
		RecalcNodes(); 
	}
		
	return true;
}//bool UIGraph::Tick(float dt)



bool UIGraph::Render(bool Force)
{

	UIWidget::Render(Force); //rendering common stuff

	if (!Force)
	{
		// look for things that could disqualify this one from being rendered
		if (!m_IsVisible)
			return false;	// disable entire widget
		if (m_pManager->GetDragTarget() == this)
			return false;	// will get rendered on top anyways
	}


	//RENDERING FILL
	if (m_GraphRectValid == false || m_CalculatedNodes.size() < 2)
		return true;

	float NodesBottomY = m_GraphTL.y + m_GraphSize.y + m_ZeroHeight;
	
	float NodeWidth = m_GraphSize.x / float(m_CalculatedNodes.size() - 1);

	int i;

	if (m_SpriteGraphFill.IsValid() == true)
	{
		for (i = 0; i < (int)m_CalculatedNodes.size() - 1; i++)
		{
			m_SpriteGraphFill.Quad_Setup(math::Vec2(m_GraphTL.x + float(i) * NodeWidth, m_CalculatedNodes.at(i)),
										math::Vec2(m_GraphTL.x + float(i+1) * NodeWidth, m_CalculatedNodes.at(i+1)),
										math::Vec2(m_GraphTL.x + float(i+1) * NodeWidth, NodesBottomY),
										math::Vec2(m_GraphTL.x + float(i) * NodeWidth, NodesBottomY));

			//m_SpriteGraphFill.Render();
			m_SpriteGraphFill.Render_PerspCorByHeight(m_GraphSize.y);
		}//END OF RENDERING FILL

		//RENDERING COLUMNS
		if (m_SpriteGraphColumn.IsValid() == true && m_CurAnimFactor > 0.0f)
		{
			//calculating cur column color
			COLOR CurColumnColor = ColorfRGBA(m_GraphColumnColor.x, m_GraphColumnColor.y, m_GraphColumnColor.z, m_GraphColumnColor.w * m_CurAnimFactor);

			m_SpriteGraphColumn.SetColor(CurColumnColor);

			for (i = 0; i < (int)m_CalculatedNodes.size(); i++)
			{
				if (i == 0 || i == m_CalculatedNodes.size() - 1 || i % m_GraphColumnEvery == 0)
				{
					m_SpriteGraphColumn.Quad_Setup(math::Vec2(m_GraphTL.x + float(i) * NodeWidth - m_GraphColumnWidth * 0.5f, m_CalculatedNodes.at(i)),
												math::Vec2(m_GraphColumnWidth, NodesBottomY - m_CalculatedNodes.at(i)));

					m_SpriteGraphColumn.Render();
				}
			}
		}
	}//END OF RENDERING COLUMNS


	//RENDERING LEVELS
	if (m_GraphLevels.empty() == false && m_SpriteGraphLevel.IsValid() == true && m_CurAnimFactor > 0.0f)
	{

		std::list<GraphLevel>::iterator LevelsIter;

		for (LevelsIter = m_GraphLevels.begin(); LevelsIter != m_GraphLevels.end(); LevelsIter++)
		{
			m_SpriteGraphLevel.SetColor(ColorfRGBA((*LevelsIter).Color.x, (*LevelsIter).Color.y, (*LevelsIter).Color.z, (*LevelsIter).Color.w * m_CurAnimFactor));
			
			m_SpriteGraphLevel.Quad_Setup(math::Vec2(m_GraphTL.x, (*LevelsIter).CalculatedValue),
										  math::Vec2(m_GraphSize.x, m_GraphLevelHeight));

			m_SpriteGraphLevel.Quad_MoveY(m_GraphLevelYShift); 

			m_SpriteGraphLevel.SetAdditive((*LevelsIter).Additive);

			m_SpriteGraphLevel.Render();
		}

	}//	if (m_GraphLevels.empty() == false && m_SpriteGraphLevel.IsValid() == true && m_CurAnimFactor > 0.0f)

	//RENDERING POINTS

	if (m_SpriteGraphPoint.IsValid() == true)
	{
		for (i = 0; i < (int)m_CalculatedNodes.size() - 1; i++)
		{
			int PointsCount;
			int PointsCountX, PointsCountY;

			float NodeHeight = abs(m_CalculatedNodes.at(i+1) - m_CalculatedNodes.at(i));

			PointsCountX = int(NodeWidth * (float)GraphicsUtil::WIDTH)+2;
			PointsCountY = int(NodeHeight * (float)GraphicsUtil::HEIGHT)+2;

			if (PointsCountX > PointsCountY)
				PointsCount = PointsCountX;
			else
				PointsCount = PointsCountY;

			PointsCount;

			if (PointsCount < 2)
				PointsCount = 2;

			for (int j = 0; j < PointsCount; j++)
			{
				math::Vec2 PointCenter;
				float PointFactor = float(j) / float(PointsCount);
				PointCenter.x = math::LinearInt(m_GraphTL.x + float(i) * NodeWidth, m_GraphTL.x + float(i+1) * NodeWidth, PointFactor)-(0.5f / (float)GraphicsUtil::WIDTH);
				PointCenter.y = math::LinearInt(m_CalculatedNodes.at(i), m_CalculatedNodes.at(i+1), PointFactor)-(0.5f / (float)GraphicsUtil::HEIGHT);

				m_SpriteGraphPoint.Quad_SetupCentered(PointCenter, m_GraphPointSize);

				m_SpriteGraphPoint.Render();
			}
		}
	}//END OF RENDERING POINTS


	//RENDERING MARKS
	if (m_SpriteGraphMark.IsValid() == true && m_GraphMarks.empty() == false)
	{
		std::list<GraphMark>::iterator MarksIter;

		for(MarksIter = m_GraphMarks.begin(); MarksIter != m_GraphMarks.end(); MarksIter++)
		{
			
			if ((*MarksIter).NodeIndex > -1 && (*MarksIter).NodeIndex < (int)m_CalculatedNodes.size())
			{

				m_SpriteGraphMark.SetColor(ColorfRGBA((*MarksIter).Color.x, (*MarksIter).Color.y, (*MarksIter).Color.z, (*MarksIter).Color.w * m_CurAnimFactor));
				m_SpriteGraphMark.SetAdditive((*MarksIter).Additive);

				m_SpriteGraphMark.Quad_SetupCentered(math::Vec2(m_GraphTL.x + float((*MarksIter).NodeIndex) * NodeWidth, m_CalculatedNodes.at((*MarksIter).NodeIndex)),
													m_GraphMarkSize * 0.5f);

				m_SpriteGraphMark.Render();
			}

		}

	}

	return true;
}//bool UIGraph::Render(bool Force)



//UIGraph: Setting graph params routines
//======================================================================================================================

void UIGraph::SetGraphNodes(const std::vector<float>& NewNodes)
{

	//setting new nodes
	if (NewNodes.size() < 2)
	{
		m_Nodes.resize(2);
		m_Nodes.at(0) = 0.0f;
		m_Nodes.at(1) = 0.0f;
		return;
	}

    m_Nodes = NewNodes;

	//recalculating nodes
	RecalcNodes(); 

}



void UIGraph::SetTLOffset(const math::Vec2& TLOffset)
{
	m_TLOffset = TLOffset;

	RecalcGraphRect(); 

	RecalcNodes();
}




void UIGraph::SetBROffset(const math::Vec2& BROffset)
{
	m_BROffset = BROffset;

	RecalcGraphRect(); 

	RecalcNodes();
}



void UIGraph::SetZeroHeight(float ZeroHeight)
{
	m_ZeroHeight = ZeroHeight;

	RecalcNodes();
}//void UIGraph::SetZeroHeight(float ZeroHeight)



//UIGraph: Calculations
//======================================================================================================================

void UIGraph::RecalcGraphRect()
{
	math::Vec2 WPos = GetPositionRelativeToParent();
	math::Vec2 WSize = GetSize();

	m_GraphTL = WPos + m_TLOffset;

	m_GraphSize = WSize - m_TLOffset - m_BROffset;
	m_GraphSize.y -= m_ZeroHeight;

	m_GraphRectValid = false;

	if (m_GraphSize.x > 0.0f && m_GraphSize.y - m_ZeroHeight > 0.0f)
		m_GraphRectValid = true;

}



void UIGraph::RecalcNodes()
{
	if (m_GraphRectValid == false)
		return;

	if (m_Nodes.size() < 2)
		return;

	m_CalculatedNodes.resize(m_Nodes.size());

	int i;

	//finding out min and max values

	if (m_RangeAuto == true)
	{
		m_Range.x = m_Nodes.at(0);
		m_Range.y = m_Nodes.at(0);



		for (i = 0; i < (int)m_Nodes.size(); i++)
		{
			if (m_Nodes.at(i) < m_Range.x)
				m_Range.x = m_Nodes.at(i);

			if (m_Nodes.at(i) > m_Range.y)
				m_Range.y = m_Nodes.at(i);
		}
	}

	float NodesInterval = m_Range.y - m_Range.x;

	//avoiding divizion by zero
	if (NodesInterval <= 0.0f)
		NodesInterval = 1.0f;



	//calculating the nodes
	for (i = 0; i < (int)m_CalculatedNodes.size(); i++)
	{
		float CurMaxHeight = m_GraphTL.y + m_GraphSize.y * (1.0f - (m_Nodes.at(i) - m_Range.x) / NodesInterval);
		float CurMinHeight = m_GraphTL.y + m_GraphSize.y + m_ZeroHeight;
		m_CalculatedNodes.at(i) = math::LinearInt(CurMinHeight, CurMaxHeight, m_CurAnimFactor); 

	}

	//calculating the levels
	if (m_GraphLevels.empty() == false)
	{
		std::list<GraphLevel>::iterator Iter;

		for (Iter = m_GraphLevels.begin(); Iter != m_GraphLevels.end(); Iter++)
			(*Iter).CalculatedValue = m_GraphTL.y + m_GraphSize.y * (1.0f - (((*Iter).Value) - m_Range.x) / NodesInterval);
	}

}//void UIGraph::RecalcNodes()



const math::Vec2& UIGraph::GetGraphNodeCoords(int Node)
{
	static math::Vec2 Result;
	Result.x = 0.0f;
	Result.y = 0.0f;

	if (Node < 0 || Node >= (int)m_CalculatedNodes.size())
		return Result;
	
	float NodeWidth = m_GraphSize.x / float(m_CalculatedNodes.size() - 1);

	Result.x = m_GraphTL.x + float(Node) * NodeWidth;
	Result.y = m_CalculatedNodes.at(Node);

	return Result;

}



void UIGraph::Register(LuaPlus::LuaState* _pScript)
{
#ifdef _ENABLE_LUABIND

    lua_State * L = _pScript->GetCState ( );

	luabind::module(L)
    [

		luabind::class_<UIGraph, UIWidget >( "GraphWidget" )
		.def( "SetGraphFillImage",		&UIGraph::SetGraphFillImage)
		.def( "SetGraphFillColor",		&UIGraph::SetGraphFillColor)
		.def( "RiseGraph",				&UIGraph::RiseGraph)
		.def( "DropGraph",				&UIGraph::DropGraph)
	];
#endif // _ENABLE_LUABIND    


}//void UIGraph::Register(LuaPlus::LuaState* _pScript)
